/**
 * \file kernel/syscall.c
 * \author Davide Gessa
 * \date 10-12-09
 */
#include <syscall.h>
#include <types.h>
#include <arch.h>
#include <debug.h>
#include <util.h>
#include <task/task.h>
#include <task/term.h>
#include <task/message.h>
#include <driver.h>
#include <video.h>
#include <driver.h>
#include <fs/fs.h>
#include <fs/file.h>
#include <fs/dir.h>
#include <main.h>
#include <debug.h>
#include <config.h>
#include <video.h>
#include <klibc/stdio.h>


/**
 * Lista delle syscalls
 */
syscall_t syscalls[] =
	{
		////////////////////////////
		// System misc 1 : 20
		{ .call = (void *) syscall_info_get },						// 0
		{ .call = (void *) syscall_test },							// 1
		{ .call = (void *) syscall_test },							// 2
		{ .call = (void *) syscall_test2 },							// 3
		{ .call = (void *) shutdown },								// 4
		{ .call = (void *) getchar },								// 5
		{ .call = (void *) putchar },								// 6
		
		{ .call = (void *) debug_ioctl },							// 7

		{ .call = (void *) syscall_void },							// 8
		{ .call = (void *) syscall_void },							// 9
		{ .call = (void *) syscall_void },							// 10
		{ .call = (void *) syscall_void },							// 11
		{ .call = (void *) syscall_void },							// 12
		{ .call = (void *) syscall_void },							// 13
		{ .call = (void *) syscall_void },							// 14
		{ .call = (void *) syscall_void },							// 15
		{ .call = (void *) syscall_void },							// 16
		{ .call = (void *) syscall_void },							// 17
		{ .call = (void *) syscall_void },							// 18
		{ .call = (void *) syscall_void },							// 19
		{ .call = (void *) syscall_void },							// 20
		
		
		///////////////////////////////
		// VFS 20 : 49
		{ .call = (void *) fs_mount },								// 21
		{ .call = (void *) fs_umount },								// 22
		{ .call = (void *) fs_iget_mount_point },					// 23
		
		{ .call = (void *) syscall_void },							// 24

		
		// Directory
		{ .call = (void *) fs_readdir },							// 25
		{ .call = (void *) fs_opendir },							// 26
		{ .call = (void *) fs_closedir },							// 27
		{ .call = (void *) fs_rewinddir },							// 28
		{ .call = (void *) fs_makedir },							// 29
		{ .call = (void *) fs_rmdir },								// 30
		{ .call = (void *) syscall_void },							// 31

		
		// File
		{ .call = (void *) fs_read },								// 32
		{ .call = (void *) fs_open },								// 33
		{ .call = (void *) fs_close },								// 34
		{ .call = (void *) fs_write },								// 35
		{ .call = (void *) fs_seek },								// 36
		{ .call = (void *) syscall_void },							// 37
		{ .call = (void *) syscall_void },							// 38
		{ .call = (void *) syscall_void },							// 39
		
		// Link
		{ .call = (void *) fs_link },								// 40
		{ .call = (void *) fs_unlink },								// 41
		
		{ .call = (void *) syscall_void },							// 42
		{ .call = (void *) syscall_void },							// 43
		{ .call = (void *) syscall_void },							// 44
		{ .call = (void *) syscall_void },							// 45
		{ .call = (void *) syscall_void },							// 46
		{ .call = (void *) syscall_void },							// 47
		{ .call = (void *) syscall_void },							// 48
		{ .call = (void *) syscall_void },							// 49
		
		
		
		//////////////////////////////
		// Network 50 : 69
		{ .call = (void *) syscall_void },							// 50
		{ .call = (void *) syscall_void },							// 51
		{ .call = (void *) syscall_void },							// 52
		{ .call = (void *) syscall_void },							// 53
		{ .call = (void *) syscall_void },							// 54
		{ .call = (void *) syscall_void },							// 55
		{ .call = (void *) syscall_void },							// 56
		{ .call = (void *) syscall_void },							// 53
		{ .call = (void *) syscall_void },							// 57
		{ .call = (void *) syscall_void },							// 58
		{ .call = (void *) syscall_void },							// 59
		{ .call = (void *) syscall_void },							// 60
		{ .call = (void *) syscall_void },							// 61
		{ .call = (void *) syscall_void },							// 62
		{ .call = (void *) syscall_void },							// 63
		{ .call = (void *) syscall_void },							// 64
		{ .call = (void *) syscall_void },							// 65
		{ .call = (void *) syscall_void },							// 66
		{ .call = (void *) syscall_void },							// 67
		{ .call = (void *) syscall_void },							// 68
		{ .call = (void *) syscall_void },							// 69
		
		
		
		//////////////////////////////
		// Task 70 : 89
		{ .call = (void *) message_clear },							// 70
		{ .call = (void *) message_send },							// 71
		{ .call = (void *) message_recv },							// 72
		
		{ .call = (void *) task_get_pid },							// 73
		{ .call = (void *) task_kill },								// 74
		{ .call = (void *) task_pause },							// 75
		{ .call = (void *) task_resume },							// 76
		{ .call = (void *) task_exec },								// 77
		
		{ .call = (void *) syscall_void },							// 78
		{ .call = (void *) syscall_void },							// 79
		{ .call = (void *) syscall_void },							// 80
		{ .call = (void *) syscall_void },							// 81
		{ .call = (void *) syscall_void },							// 82
		{ .call = (void *) syscall_void },							// 83
		{ .call = (void *) syscall_void },							// 84
		{ .call = (void *) syscall_void },							// 85
		{ .call = (void *) syscall_void },							// 86
		{ .call = (void *) syscall_void },							// 87
		{ .call = (void *) syscall_void },							// 88
		{ .call = (void *) syscall_void },							// 89
		

		//////////////////////////////
		// Driver 90 : 99
		{ .call = (void *) driver_add },							// 90
		{ .call = (void *) driver_del },							// 91
		{ .call = (void *) driver_iget },							// 92
		{ .call = (void *) driver_initd },							// 93
		{ .call = (void *) driver_freed },							// 94
		
		{ .call = (void *) syscall_void },							// 95
		{ .call = (void *) syscall_void },							// 96
		{ .call = (void *) syscall_void },							// 97
		{ .call = (void *) syscall_void },							// 98
		{ .call = (void *) syscall_void },							// 99
		
		
		//////////////////////////////
		// Device 100 : 109
		{ .call = (void *) device_iget },							// 100
		{ .call = (void *) device_update },							// 101
				
	};


/**
 * Numero totale delle syscalls
 */
uint32_t syscalls_number = sizeof(syscalls) / sizeof(syscall_t);


/**
 * Effetua una chiamata ad una call
 * 
 * \param call numero della syscall
 * \param ebx parametro 1
 * \param ecx parametro 2
 * \param edx parametro 3
 */
int syscall_call(uint32_t call, uint32_t ebx, uint32_t ecx, uint32_t edx)
{
	regs_t reg;
	reg.eax = call;
	reg.ebx = ebx; 
	reg.ecx = ecx; 
	reg.edx = edx; 
	asm("movl %0, %%eax\n\t": : "g" (call));
	asm("movl %0, %%ebx\n\t": : "g" (reg.ebx));
	asm("movl %0, %%ebx\n\t": : "g" (reg.ecx));
	asm("movl %0, %%ebx\n\t": : "g" (reg.edx));
	
	asm ("int %0\n" : : "g" (SYSCALL_INT));
	
	asm ("movl %%eax, %0\n\t" : "=r" (reg.eax));
	return reg.eax;
}


/**
 * Handler delle chiamate alla syscall
 * 
 * \param r tutti i registri salvati al momento dell'interruzione
 */
void syscall_callback(regs_t *r)
{	
	regs_t reg;
	int ret;
	
		
	// Preleva gli argomenti
	asm ("movl %%eax, %0\n\t" : "=r" (reg.eax));
	asm ("movl %%ebx, %0\n\t" : "=r" (reg.ebx));
	asm ("movl %%ecx, %0\n\t" : "=r" (reg.ecx));	
	asm ("movl %%edx, %0\n\t" : "=r" (reg.edx));

	//printf("%d, %d, %d, %d\n", reg.eax, reg.ebx, reg.ecx, reg.edx);

	if(reg.eax > syscalls_number)
		return;
		
	ret = syscalls[reg.eax].call(reg.ebx, reg.ecx, reg.edx);		
	
	// Restituisce il valore di ritorno
	asm("movl %0, %%eax\n\t": : "g" (ret)); 
}



/**
 * Inizializza le syscall
 */
void syscall_init()
{
	DEBUGP("syscall_init()\n");
}





/**
 * Syscall di test
 */
uint32_t syscall_test()
{
	puts("syscall test!\n");
	
	return 0;
}


/**
 * Syscall di test2
 */
uint32_t syscall_test2()
{
	puts("syscall test2!\n");
	
	return 0;
}


/**
 * Syscall vuota
 */
uint32_t syscall_void()
{
	return 0;
}



/**
 * Restituisce le info del sistema
 * 
 * \param ki puntatore ad una struttura sysinfo
 */
uint32_t syscall_info_get(sysinfo_t *ki)
{
	strcpy(ki->name, NAME);
	strcpy(ki->ver, VER);
	strcpy(ki->rev, REV);
	strcpy(ki->date, __DATE__);
	strcpy(ki->time, __TIME__);
	strcpy(ki->arch, ARCH);
	strcpy(ki->vend, CPU_VENDOR);
}
